#ifndef COMMON_SOURCE_CPP_CAST_UTILITS_H_
#define COMMON_SOURCE_CPP_CAST_UTILITS_H_
#include <type_traits>
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
#include <ctime>
#include <string>
#include <map>
#include <list>
#include <set>
#include <sstream>
#include <chrono>
/** 类型转换模板函数族，实现通用类型L->R的转换 */
namespace net {
	namespace gdface {
		namespace utils {
			// 判断类型是否可以强制转换
			template<typename T>
			struct is_force_cast : std::integral_constant < bool,std::is_arithmetic<T>::value || std::is_enum<T>::value> {};
			// 相同类型直接转发
			template<typename L>
			void cast(const L &left, L &right) {
				right = left;
			}
			template<typename L>
			L
			cast(L left, typename std::decay<L>::type *right) {
				return std::forward<L>(left);
			}
			// 数字类型和enum都使用强制类型转换
			template<typename L, typename R>
			typename std::enable_if < !std::is_same<L, R>::value && is_force_cast<L>::value && is_force_cast<R>::value, void > ::type
			cast(L left, R &right) {
				right = (R)left;
			}
			template<typename L, typename R>
			typename std::enable_if < !std::is_same<L, R>::value && is_force_cast<L>::value && is_force_cast<R>::value, R > ::type
			cast(L left, R*right) {
				return (R)left;
			}
			// number -> string
			template<typename L>
			typename std::enable_if<std::is_arithmetic<L>::value, void>::type
			cast(L left, std::string &right) {
				std::ostringstream ss;
				ss << left;
				right = ss.str();
			}
			template<typename L>
			typename std::enable_if<std::is_arithmetic<L>::value, std::string>::type
			cast(L left, std::string *) {
				std::string right;
				cast(left, right);
				return right;
			}
			// string -> number
			template<typename R>
			typename std::enable_if<std::is_arithmetic<R>::value, void>::type
			cast(const std::string &left, R& r) {
				std::istringstream ss(left);
				ss >> r;
			}
			template<typename R>
			typename std::enable_if<std::is_arithmetic<R>::value, R>::type
			cast(const std::string &left, R *) {
				R right;
				cast(left, right);
				return right;
			}
			// uint64_t(milliseconds) to tm
			void
			inline cast(uint64_t left, std::tm&right) {
				auto mTime = std::chrono::milliseconds(left);
				auto tp = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>(mTime);
				auto tt = std::chrono::system_clock::to_time_t(tp);
				right = *std::localtime(&tt);
			}

			std::tm
			inline cast(uint64_t left, std::tm*) {
				std::tm right;
				cast(left, right);
				return right;
			}
			void
			inline cast(const std::tm &left, uint64_t&right) {
				auto l = left;
				auto time = std::mktime(&l) * 1000;
				right = (uint64_t)time;
			}
			uint64_t
			inline cast(const std::tm &left, uint64_t*) {
				uint64_t right;
				cast(left, right);
				return right;
			}
			// int64_t(milliseconds) to tm
			void
			inline cast(int64_t left, std::tm&right) {
				auto mTime = std::chrono::milliseconds(left);
				auto tp = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>(mTime);
				auto tt = std::chrono::system_clock::to_time_t(tp);
				right = *std::localtime(&tt);
			}
			std::tm
			inline cast(int64_t left, std::tm*) {
				std::tm right;
				cast(left, right);
				return right;
			}
			// tm to int64_t(milliseconds)
			void
			inline cast(const std::tm &left, int64_t&right) {
				auto l = left;
				time_t time = std::mktime(&l) * 1000;
				right = (int64_t)time;
			}

			int64_t
			inline cast(const std::tm &left, int64_t*) {
				int64_t right;
				cast(left, right);
				return right;
			}
			// string --> vector<uint8_t>
			void
			inline cast(const std::vector<uint8_t> &left, std::string&right) {
				right.assign(left.begin(), left.end());
			}
			std::string
			inline cast(const std::vector<uint8_t> &left, std::string*) {			
				return std::string(left.begin(), left.end());
			}
			// vector<uint8_t> --> string
			void
			inline cast(const std::string &left, std::vector<uint8_t>&right) {
				right.assign(left.begin(), left.end());
			}

			std::vector<uint8_t>
			inline cast(const std::string &left, std::vector<uint8_t>*) {
				return std::vector<uint8_t>(left.begin(),left.end());
			}
			// list cast
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, void>::type
			cast(const std::list<L>&left, std::list<R>& right) {
				for (auto l : left) {
					R r;
					cast(l, r);
					right.emplace_back(r);
				}
			}

			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, std::list<R>>::type
			cast(std::list<L>&&left, std::list<R>*) {
				std::list<R> right;
				std::transform(left.begin(), left.end(), std::back_inserter(right), [](L l)->R {return cast(std::move(l), (R*)nullptr); });
				return std::move(right);
			}
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, std::list<R>>::type
			cast(const std::list<L>&left, std::list<R>*) {
				std::list<R> right;
				cast(left, right);
				return std::move(right);
			}

			// set cast
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, void>::type
			cast(const std::set<L>&left, std::set<R>&right) {
				std::transform(left.begin(), left.end(), std::insert_iterator<std::set<R>>(right, right.begin()), [](L l)->R {return cast(l, (R*)nullptr); });
			}

			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, std::set<R>>::type
			cast(std::set<L>&&left, std::set<R>*) {
				std::set<R> right;
				std::transform(left.begin(), left.end(), std::insert_iterator<std::set<R>>(right, right.begin()), [](L l)->R {return cast(std::move(l), (R*)nullptr); });
				return std::move(right);
			}
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, std::set<R>>::type
			cast(const std::set<L>&left, std::set<R>*) {
				std::set<R> right;
				cast(left, right);
				return std::move(right);
			}
			// vector cast
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, void>::type
			cast(const std::vector<L>&left, std::vector<R>& right) {
				std::transform(left.begin(), left.end(), std::insert_iterator<std::vector<R>>(right, right.begin()), [](L l)->R {return cast(l, (R*)nullptr); });
			}
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, std::vector<R>>::type
			cast(std::vector<L>&&left, std::vector<R>*) {
				std::vector<R> right;
				std::transform(left.begin(), left.end(), std::insert_iterator<std::vector<R>>(right, right.begin()), [](L l)->R {return cast(std::move(l), (R*)nullptr); });
				return std::move(right);
			}
			template<typename L, typename R>
			typename std::enable_if<!std::is_same<L, R>::value, std::vector<R>>::type
			cast(const std::vector<L>&left, std::vector<R>*) {
				std::vector<R> right;
				cast(left, right);
				return std::move(right);
			}
			// map cast
			template<typename KL, typename VL, typename KR, typename VR>
			typename std::enable_if<!std::is_same<KL, KR>::value || !std::is_same<VL, VR>::value, void>::type
			cast(const std::map<KL, VL>&left, std::map<KR, VR>&right) {
				std::transform(left.begin(), left.end(), std::insert_iterator<std::map<KR, VR>>(right, right.begin()),
					[](std::pair<KL, VL> l)->std::pair<KR, VR> {return std::pair<KR, VR>(cast(l.first, (KR*)nullptr), cast(l.second, (VR*)nullptr)); });
			}
			template<typename KL, typename VL, typename KR, typename VR>
			typename std::enable_if<!std::is_same<KL, KR>::value || !std::is_same<VL, VR>::value, std::map<KR, VR>>::type
			cast(std::map<KL, VL>&&left, std::map<KR, VR>*) {
				std::map<KR, VR> right;
				std::transform(left.begin(), left.end(), std::insert_iterator<std::map<KR, VR>>(right, right.begin()),
					[](std::pair<KL, VL> l)->std::pair<KR, VR> {return std::pair<KR, VR>(cast(std::move(l.first), (KR*)nullptr), cast(std::move(l.second), (VR*)nullptr)); });
				return std::move(right);
			}
			template<typename KL, typename VL, typename KR, typename VR>
			typename std::enable_if<!std::is_same<KL, KR>::value || !std::is_same<VL, VR>::value, std::map<KR, VR>>::type
			cast(const std::map<KL, VL>&left, std::map<KR, VR>*) {
				std::map<KR, VR> right;
				cast(left, right);
				return std::move(right);
			}
		} /* namespace utils */
	} /* namespace gdface */
} /* namespace net */
#endif // !COMMON_SOURCE_CPP_CAST_UTILITS_H_

